Skip to content

Feature/flatpak update portal #117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from

Conversation

purejava
Copy link
Contributor

Copy link

coderabbitai bot commented Jul 31, 2025

Walkthrough

The changes introduce Flatpak update integration for the Linux distribution of the application. The pom.xml is updated to include a new dependency on org.purejava:flatpak-update-portal version 1.0.0 and to update the integrations API version to 1.7.0-SNAPSHOT. The module descriptor (module-info.java) is modified to require the new portal module, open the org.cryptomator.linux.update package for reflective access, and provide the new FlatpakUpdater as a service provider for the UpdateMechanism interface. A new class, FlatpakUpdater, is added, implementing update checking, triggering, spawning, and monitoring via the Flatpak update portal and DBus signals. A service provider configuration file is also added to register this updater as an implementation of the UpdateService interface.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Complexity involves reviewing a new integration class with asynchronous DBus signal handling, concurrency control, dependency and module configuration, and service registration. The main review effort is centered on the new FlatpakUpdater class and its integration into the update service framework.

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2aa7b15 and 08a7742.

📒 Files selected for processing (1)
  • src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java (2)

49-52: Remove redundant CreateUpdateMonitor call in constructor.

The CreateUpdateMonitor call in the constructor appears redundant as it's called again in getUpdateMonitor() method, and its return value is ignored here.

 public FlatpakUpdater() {
     this.portal = new UpdatePortal();
-    portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY);
 }

173-193: Consider extracting a helper method to reduce code duplication.

The pattern of extracting string values from variants is repeated multiple times.

+private String extractStringFromVariant(Map<String, Variant<?>> map, String key) {
+    Variant<?> variant = map.get(key);
+    return variant != null ? (String) variant.getValue() : "";
+}
+
 private void notifyOnUpdateAvailable(Flatpak.UpdateMonitor.UpdateAvailable signal) {
-    String remoteCommit = "";
-    Variant<?> remoteCommitVariant = signal.update_info.get("remote-commit");
-    if (null != remoteCommitVariant) {
-        remoteCommit = (String) remoteCommitVariant.getValue();
-    }
-    String runningCommit = "";
-    Variant<?> runningCommitVariant = signal.update_info.get("running-commit");
-    if (null != runningCommitVariant) {
-        runningCommit = (String) runningCommitVariant.getValue();
-    }
-    String localCommit = "";
-    Variant<?> localCommitVariant = signal.update_info.get("local-commit");
-    if (null != localCommitVariant) {
-        localCommit = (String) localCommitVariant.getValue();
-    }
+    String remoteCommit = extractStringFromVariant(signal.update_info, "remote-commit");
+    String runningCommit = extractStringFromVariant(signal.update_info, "running-commit");
+    String localCommit = extractStringFromVariant(signal.update_info, "local-commit");
     UpdateAvailable updateAvailable = new UpdateAvailable(runningCommit, localCommit, remoteCommit);
     for (UpdateAvailableListener listener : updateAvailableListeners) {
         listener.onUpdateAvailable(updateAvailable);
     }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48d0261 and a2607d7.

📒 Files selected for processing (4)
  • pom.xml (2 hunks)
  • src/main/java/module-info.java (2 hunks)
  • src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java (1 hunks)
  • src/main/resources/META-INF/services/org.cryptomator.integrations.update.UpdateService (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
src/main/java/module-info.java (1)

Learnt from: infeo
PR: #80
File: src/main/java/org/cryptomator/linux/autostart/FreedesktopAutoStartService.java:56-62
Timestamp: 2024-07-16T22:36:32.769Z
Learning: For the FreedesktopAutoStartService in the Cryptomator project, exceptions are preferred to contain all necessary debugging information without additional logging before throwing them.

src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java (1)

Learnt from: infeo
PR: #80
File: src/main/java/org/cryptomator/linux/autostart/FreedesktopAutoStartService.java:56-62
Timestamp: 2024-07-16T22:36:32.769Z
Learning: For the FreedesktopAutoStartService in the Cryptomator project, exceptions are preferred to contain all necessary debugging information without additional logging before throwing them.

🔇 Additional comments (4)
src/main/resources/META-INF/services/org.cryptomator.integrations.update.UpdateService (1)

1-1: LGTM!

The service provider configuration correctly registers the FlatpakUpdater implementation.

pom.xml (2)

43-43: Verify the use of SNAPSHOT version for production readiness.

The PR depends on a SNAPSHOT version of integrations-api. If this PR is intended for production, consider waiting for the stable release of version 1.7.0.


46-46: LGTM!

The new Flatpak update portal dependency is properly configured with a stable version.

Also applies to: 92-96

src/main/java/module-info.java (1)

6-6: LGTM!

The module configuration correctly integrates the new Flatpak update service with proper dependencies, service provisions, and package access.

Also applies to: 9-9, 14-14, 22-22, 31-31, 36-36

Comment on lines 60 to 67
public UpdateCheckerTask getLatestReleaseChecker(DistributionChannel.Value channel) {
if (channel != DistributionChannel.Value.LINUX_FLATPAK) {
LOG.error("Wrong channel provided: {}", channel);
return null;
}
portal.setUpdateCheckerTaskFor(APP_NAME);
return portal.getUpdateCheckerTaskFor(APP_NAME);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Throw exception instead of returning null for invalid channel.

Returning null for an invalid channel could lead to NullPointerException in the calling code. Consider throwing an IllegalArgumentException instead.

 @Override
 public UpdateCheckerTask getLatestReleaseChecker(DistributionChannel.Value channel) {
     if (channel != DistributionChannel.Value.LINUX_FLATPAK) {
-        LOG.error("Wrong channel provided: {}", channel);
-        return null;
+        throw new IllegalArgumentException("Wrong channel provided: " + channel + ". Expected: LINUX_FLATPAK");
     }
     portal.setUpdateCheckerTaskFor(APP_NAME);
     return portal.getUpdateCheckerTaskFor(APP_NAME);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public UpdateCheckerTask getLatestReleaseChecker(DistributionChannel.Value channel) {
if (channel != DistributionChannel.Value.LINUX_FLATPAK) {
LOG.error("Wrong channel provided: {}", channel);
return null;
}
portal.setUpdateCheckerTaskFor(APP_NAME);
return portal.getUpdateCheckerTaskFor(APP_NAME);
}
@Override
public UpdateCheckerTask getLatestReleaseChecker(DistributionChannel.Value channel) {
if (channel != DistributionChannel.Value.LINUX_FLATPAK) {
throw new IllegalArgumentException("Wrong channel provided: " + channel + ". Expected: LINUX_FLATPAK");
}
portal.setUpdateCheckerTaskFor(APP_NAME);
return portal.getUpdateCheckerTaskFor(APP_NAME);
}
🤖 Prompt for AI Agents
In src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java around lines
60 to 67, the method getLatestReleaseChecker returns null when an invalid
channel is provided, which risks causing NullPointerExceptions. Replace the null
return with throwing an IllegalArgumentException that clearly states the channel
is invalid. This change ensures callers are immediately informed of the misuse
and can handle the error properly.

Comment on lines 70 to 73
public void triggerUpdate() throws UpdateFailedException {
var monitor = getUpdateMonitor();
portal.updateApp("x11:0", monitor, UpdatePortal.OPTIONS_DUMMY);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid hardcoding the display parameter.

The hardcoded "x11:0" display parameter may not work correctly in Wayland environments or other display configurations.

Consider obtaining the display parameter dynamically:

 @Override
 public void triggerUpdate() throws UpdateFailedException {
     var monitor = getUpdateMonitor();
-    portal.updateApp("x11:0", monitor, UpdatePortal.OPTIONS_DUMMY);
+    String display = System.getenv("DISPLAY");
+    if (display == null || display.isEmpty()) {
+        display = System.getenv("WAYLAND_DISPLAY");
+        if (display != null && !display.isEmpty()) {
+            display = "wayland:" + display;
+        } else {
+            display = "x11:0"; // fallback
+        }
+    }
+    portal.updateApp(display, monitor, UpdatePortal.OPTIONS_DUMMY);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void triggerUpdate() throws UpdateFailedException {
var monitor = getUpdateMonitor();
portal.updateApp("x11:0", monitor, UpdatePortal.OPTIONS_DUMMY);
}
@Override
public void triggerUpdate() throws UpdateFailedException {
var monitor = getUpdateMonitor();
String display = System.getenv("DISPLAY");
if (display == null || display.isEmpty()) {
display = System.getenv("WAYLAND_DISPLAY");
if (display != null && !display.isEmpty()) {
display = "wayland:" + display;
} else {
display = "x11:0"; // fallback
}
}
portal.updateApp(display, monitor, UpdatePortal.OPTIONS_DUMMY);
}
🤖 Prompt for AI Agents
In src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java around lines
70 to 73, the display parameter "x11:0" is hardcoded in the portal.updateApp
call, which can cause issues in Wayland or other display environments. Modify
the code to dynamically obtain the display parameter from the environment or
system properties instead of using a fixed string. Replace the hardcoded "x11:0"
with a variable that retrieves the current display setting at runtime.

Comment on lines 98 to 108
@Override
public void close() throws Exception {
try {
if (null != updateMonitor) {
portal.cancelUpdateMonitor(updateMonitor);
}
portal.close();
} catch (Exception e) {
LOG.error(e.toString(), e.getCause());
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve exception handling in close method.

The current implementation catches a generic Exception and the logging could be more informative.

 @Override
 public void close() throws Exception {
     try {
         if (null != updateMonitor) {
             portal.cancelUpdateMonitor(updateMonitor);
         }
         portal.close();
-    } catch (Exception e) {
-        LOG.error(e.toString(), e.getCause());
+    } catch (DBusException e) {
+        LOG.error("Failed to close DBus connection properly", e);
+        throw e;
+    } catch (Exception e) {
+        LOG.error("Unexpected error during resource cleanup", e);
+        throw e;
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public void close() throws Exception {
try {
if (null != updateMonitor) {
portal.cancelUpdateMonitor(updateMonitor);
}
portal.close();
} catch (Exception e) {
LOG.error(e.toString(), e.getCause());
}
}
@Override
public void close() throws Exception {
try {
if (null != updateMonitor) {
portal.cancelUpdateMonitor(updateMonitor);
}
portal.close();
} catch (DBusException e) {
LOG.error("Failed to close DBus connection properly", e);
throw e;
} catch (Exception e) {
LOG.error("Unexpected error during resource cleanup", e);
throw e;
}
}
🤖 Prompt for AI Agents
In src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java around lines
98 to 108, improve the exception handling in the close method by catching more
specific exceptions if possible, and enhance the logging to provide a clearer
and more informative message. Instead of logging e.toString() and e.getCause()
separately, log the full exception with its stack trace to capture complete
context for debugging.

Comment on lines 195 to 226
private void notifyOnUpdateProceeds(Flatpak.UpdateMonitor.Progress signal) {
long status = ((UInt32) signal.info.get("status").getValue()).longValue();
long progress = 0;
Variant<?> progressVariant = signal.info.get("progress");
if (null != progressVariant) {
progress = ((UInt32) progressVariant.getValue()).longValue();
}
long nOps = -1;
Variant<?> nOpsVariant = signal.info.get("n_ops");
if (null != nOpsVariant) {
nOps = ((UInt32) nOpsVariant.getValue()).longValue();
}
long oP = -1;
Variant<?> oPVariant = signal.info.get("op");
if (null != oPVariant) {
oP = ((UInt32) oPVariant.getValue()).longValue();
}
String error = "";
Variant<?> errorVariant = signal.info.get("error");
if (null != errorVariant) {
error = (String) errorVariant.getValue();
}
String errorMessage = "";
Variant<?> errorMessageVariant = signal.info.get("error_message");
if (null != errorMessageVariant) {
errorMessage = (String) errorMessageVariant.getValue();
}
Progress p = new Progress(nOps, oP, status, progress, error, errorMessage);
for (ProgressListener listener : progressListeners) {
listener.onProgress(p);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add type safety checks and reduce code duplication.

The direct cast without type checking could throw ClassCastException. Also, the code has repetitive patterns.

+private long extractLongFromVariant(Map<String, Variant<?>> map, String key, long defaultValue) {
+    Variant<?> variant = map.get(key);
+    if (variant != null && variant.getValue() instanceof UInt32) {
+        return ((UInt32) variant.getValue()).longValue();
+    }
+    return defaultValue;
+}
+
 private void notifyOnUpdateProceeds(Flatpak.UpdateMonitor.Progress signal) {
-    long status = ((UInt32) signal.info.get("status").getValue()).longValue();
-    long progress = 0;
-    Variant<?> progressVariant = signal.info.get("progress");
-    if (null != progressVariant) {
-        progress = ((UInt32) progressVariant.getValue()).longValue();
-    }
-    long nOps = -1;
-    Variant<?> nOpsVariant = signal.info.get("n_ops");
-    if (null != nOpsVariant) {
-        nOps = ((UInt32) nOpsVariant.getValue()).longValue();
-    }
-    long oP = -1;
-    Variant<?> oPVariant = signal.info.get("op");
-    if (null != oPVariant) {
-        oP = ((UInt32) oPVariant.getValue()).longValue();
-    }
-    String error = "";
-    Variant<?> errorVariant = signal.info.get("error");
-    if (null != errorVariant) {
-        error = (String) errorVariant.getValue();
-    }
-    String errorMessage = "";
-    Variant<?> errorMessageVariant = signal.info.get("error_message");
-    if (null != errorMessageVariant) {
-        errorMessage = (String) errorMessageVariant.getValue();
-    }
+    long status = extractLongFromVariant(signal.info, "status", 0);
+    long progress = extractLongFromVariant(signal.info, "progress", 0);
+    long nOps = extractLongFromVariant(signal.info, "n_ops", -1);
+    long oP = extractLongFromVariant(signal.info, "op", -1);
+    String error = extractStringFromVariant(signal.info, "error");
+    String errorMessage = extractStringFromVariant(signal.info, "error_message");
     Progress p = new Progress(nOps, oP, status, progress, error, errorMessage);
     for (ProgressListener listener : progressListeners) {
         listener.onProgress(p);
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java lines 195 to
226, the method notifyOnUpdateProceeds directly casts values from signal.info
without type checks, risking ClassCastException, and repeats similar code
patterns. Refactor by adding type checks before casting to ensure safety, and
extract the repeated pattern of retrieving and casting values from the map into
a helper method to reduce duplication and improve readability.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java (4)

59-62: Avoid hardcoding the display parameter.

The hardcoded "x11:0" display parameter may not work correctly in Wayland environments or other display configurations.

Consider obtaining the display parameter dynamically:

 @Override
 public void triggerUpdate() throws UpdateFailedException {
 	var monitor = getUpdateMonitor();
-	portal.updateApp("x11:0", monitor, UpdatePortal.OPTIONS_DUMMY);
+	String display = System.getenv("DISPLAY");
+	if (display == null || display.isEmpty()) {
+		display = System.getenv("WAYLAND_DISPLAY");
+		if (display != null && !display.isEmpty()) {
+			display = "wayland:" + display;
+		} else {
+			display = "x11:0"; // fallback
+		}
+	}
+	portal.updateApp(display, monitor, UpdatePortal.OPTIONS_DUMMY);
 }

82-92: Improve exception handling in close method.

The current implementation catches a generic Exception and the logging could be more informative. Additionally, exceptions should not be swallowed in close() method as per AutoCloseable contract.

 @Override
 public void close() throws Exception {
 	try {
 		if (null != updateMonitor) {
 			portal.cancelUpdateMonitor(updateMonitor);
 		}
 		portal.close();
-	} catch (Exception e) {
-		LOG.error(e.toString(), e.getCause());
+	} catch (DBusException e) {
+		LOG.error("Failed to close DBus connection properly", e);
+		throw e;
+	} catch (Exception e) {
+		LOG.error("Unexpected error during resource cleanup", e);
+		throw e;
 	}
 }

124-135: Add type safety checks to prevent ClassCastException.

The direct cast without type checking could throw ClassCastException. Add proper type validation before casting.

 private void notifyOnUpdateProceeds(Flatpak.UpdateMonitor.Progress signal) {
-	long status = ((UInt32) signal.info.get("status").getValue()).longValue();
+	long status = 0;
+	Variant<?> statusVariant = signal.info.get("status");
+	if (statusVariant != null && statusVariant.getValue() instanceof UInt32) {
+		status = ((UInt32) statusVariant.getValue()).longValue();
+	} else {
+		LOG.warn("Missing or invalid status in update progress signal");
+	}
+	
 	long progress = 0;
 	Variant<?> progressVariant = signal.info.get("progress");
-	if (null != progressVariant) {
+	if (progressVariant != null && progressVariant.getValue() instanceof UInt32) {
 		progress = ((UInt32) progressVariant.getValue()).longValue();
 	}
 	Progress p = new Progress(status, progress);
 	for (ProgressListener listener : progressListeners) {
 		listener.onProgress(p);
 	}
 }

94-112: Handle monitor creation failure more robustly.

The method can return null if monitor creation fails, which could cause NullPointerException in triggerUpdate(). Additionally, only the Progress signal handler is registered, missing the UpdateAvailable handler mentioned in past reviews.

-private synchronized Flatpak.UpdateMonitor getUpdateMonitor() {
+private synchronized Flatpak.UpdateMonitor getUpdateMonitor() throws UpdateFailedException {
 	if (updateMonitor == null) {
 		var updateMonitorPath = portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY);
 		if (updateMonitorPath != null) {
 			LOG.debug("UpdateMonitor successful created at {}", updateMonitorPath);
 			updateMonitor = portal.getUpdateMonitor(updateMonitorPath.toString());
 			try {
+				portal.getDBusConnection().addSigHandler(Flatpak.UpdateMonitor.UpdateAvailable.class, signal -> {
+					LOG.info("Update available signal received");
+					// Handle update available notification if needed
+				});
 				portal.getDBusConnection().addSigHandler(Flatpak.UpdateMonitor.Progress.class, signal -> {
 					notifyOnUpdateProceeds(signal);
 				});
 			} catch (DBusException e) {
-				LOG.error(e.toString(), e.getCause());
+				updateMonitor = null; // Reset on error
+				throw new UpdateFailedException("Failed to register DBus signal handlers", e);
 			}
 		} else {
-			LOG.error("Failed to create UpdateMonitor on DBus");
+			throw new UpdateFailedException("Failed to create UpdateMonitor on DBus");
 		}
 	}
 	return updateMonitor;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a2607d7 and 4976dde.

📒 Files selected for processing (1)
  • src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: for the `freedesktopautostartservice` in the cryptomator project, exceptions are preferred to contai...
Learnt from: infeo
PR: cryptomator/integrations-linux#80
File: src/main/java/org/cryptomator/linux/autostart/FreedesktopAutoStartService.java:56-62
Timestamp: 2024-07-16T22:36:32.769Z
Learning: For the `FreedesktopAutoStartService` in the Cryptomator project, exceptions are preferred to contain all necessary debugging information without additional logging before throwing them.

Applied to files:

  • src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java

Comment on lines +42 to +45
public FlatpakUpdater() {
this.portal = new UpdatePortal();
portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove unnecessary monitor creation in constructor.

The constructor creates an update monitor but doesn't check or use the result. This monitor should be created lazily when first needed via getUpdateMonitor().

 public FlatpakUpdater() {
 	this.portal = new UpdatePortal();
-	portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public FlatpakUpdater() {
this.portal = new UpdatePortal();
portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY);
}
public FlatpakUpdater() {
this.portal = new UpdatePortal();
}
🤖 Prompt for AI Agents
In src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java between lines
42 and 45, remove the call to
portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY) from the constructor
since the update monitor is created but never used there. Instead, implement
lazy initialization of the update monitor inside the getUpdateMonitor() method
so that the monitor is only created when first accessed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java (2)

41-44: Remove redundant monitor creation in constructor

portal.CreateUpdateMonitor(...) is invoked eagerly but its result is ignored and a fresh monitor is created later in prepareUpdate(), wasting a DBus round-trip. Drop this call and lazily create the monitor only when needed.


99-106: Handle potential DBusException from updateApp and make display selection dynamic

  1. portal.updateApp(...) can throw DBusException; wrap the call in try/catch and fail the latch accordingly.
  2. The display parameter is hard-coded to "x11:0", which breaks on Wayland or non-standard X11 displays. Obtain it from DISPLAY / WAYLAND_DISPLAY env vars with a fallback.
🧹 Nitpick comments (1)
src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java (1)

109-116: Null-safety for signal map access

signal.info.get("progress") is accessed without null-check before the cast at Line 114. Although unlikely, a missing key would raise NullPointerException. Guard with a null check:

Variant<?> progressVariant = signal.info.get("progress");
if (progressVariant != null && progressVariant.getValue() instanceof UInt32 u) {
    progress = u.doubleValue() / 100.0;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4976dde and 2aa7b15.

📒 Files selected for processing (2)
  • src/main/java/module-info.java (2 hunks)
  • src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/module-info.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-07-16T22:36:32.769Z
Learnt from: infeo
PR: cryptomator/integrations-linux#80
File: src/main/java/org/cryptomator/linux/autostart/FreedesktopAutoStartService.java:56-62
Timestamp: 2024-07-16T22:36:32.769Z
Learning: For the `FreedesktopAutoStartService` in the Cryptomator project, exceptions are preferred to contain all necessary debugging information without additional logging before throwing them.

Applied to files:

  • src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java

Comment on lines 51 to 73
@Override
public boolean isUpdateAvailable() {
var cdl = new CountDownLatch(1);
portal.setUpdateCheckerTaskFor(APP_NAME);
var checkTask = portal.getUpdateCheckerTaskFor(APP_NAME);
var updateAvailable = new AtomicBoolean(false);
checkTask.setOnSucceeded(latestVersion -> {
updateAvailable.set(true); // TODO: compare version strings before setting this to true
cdl.countDown();
});
checkTask.setOnFailed(error -> {
LOG.warn("Error while checking for updates.", error);
cdl.countDown();
});
try {
cdl.await();
return updateAvailable.get();
} catch (InterruptedException e) {
checkTask.cancel();
Thread.currentThread().interrupt();
return false;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add timeout and proper version comparison in update check

isUpdateAvailable() blocks indefinitely on cdl.await() and sets the flag to true without validating that latestVersion is newer than the running one.
• Specify a sensible timeout (e.g. 30 s) and abort gracefully.
• Compare latestVersion with BuildConfig.version() (or similar) before returning true.

🤖 Prompt for AI Agents
In src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java between lines
51 and 73, the isUpdateAvailable() method currently blocks indefinitely on
cdl.await() and sets updateAvailable to true without verifying if the
latestVersion is actually newer than the current version. To fix this, add a
timeout (e.g., 30 seconds) to the cdl.await() call to prevent indefinite
blocking and handle the timeout case gracefully. Also, modify the onSucceeded
handler to compare the received latestVersion string with the current version
from BuildConfig.version() (or equivalent) and only set updateAvailable to true
if the latestVersion is newer.

Comment on lines +154 to +158
portal.cancelUpdateMonitor(monitor);
stopReceivingSignals();
portal.close(); // TODO: is this right? belongs to parent class. update can not be retried afterwards. or should each process have its own portal instance?
error = new UpdateFailedException("Update cancelled by user");
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Portal lifecycle & resource leak

cancel() closes the shared portal, but the normal success path (applyUpdate) never does, leaving the DBus connection open for the lifetime of the JVM. Either:
• Make FlatpakUpdateProcess own a dedicated UpdatePortal and always close it in finally, or
• Let FlatpakUpdater implement AutoCloseable and close the portal when the application shuts down.

🤖 Prompt for AI Agents
In src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java around lines
154 to 158, the shared portal is closed on cancel but not on successful update,
causing a resource leak. To fix this, either refactor FlatpakUpdateProcess to
own a dedicated UpdatePortal instance and ensure it is closed in a finally block
after update attempts, or modify FlatpakUpdater to implement AutoCloseable and
close the shared portal when the application shuts down, ensuring the DBus
connection is properly released in all cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants